package com.sojson.util.pay.ali;

import java.io.IOException;
import java.math.BigDecimal;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import com.alipay.api.AlipayResponse;
import com.alipay.api.domain.AlipayFundTransOrderQueryModel;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeCloseModel;
import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.domain.AlipayUserInfoAuthModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipayFundTransOrderQueryResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.alipay.api.response.AlipayUserInfoAuthResponse;
import com.ijpay.alipay.AliPayApi;
import com.sojson.base.impl.BaseOrder;
import com.sojson.config.exception.TransErrorCode;
import com.sojson.config.exception.bean.Status;
import com.sojson.constant.Constant;
import com.sojson.constant.ConstantCache;
import com.sojson.constant.IpConfig;
import com.sojson.util.ExpUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.SystemParamBaseUtil;
import com.sojson.util.cache.CacheUtil;
import com.sojson.util.encrypt.bothway.alike.base64.Base64Util;
import com.sojson.util.json.JsonUtil;
import com.sojson.util.pay.ali.bean.AliResponse;
import com.sojson.util.pay.ali.bean.AliResult;
import com.sojson.util.pay.ali.constant.AliConstant;
import com.sojson.util.pay.ali.enums.EAliCallBackType;
import com.sojson.util.servlet.ServletUtil;
import com.sojson.util.token.TokenUtil;

/**
 * 支付宝工具类
 *
 * @author liu
 * @date 2020-11-02
 */
public class AliUtil {

    /** ----------------------------以下是证书私钥---------------------------- */
    /** ----------------------------以下是证书私钥---------------------------- */
    /** ----------------------------以下是证书私钥---------------------------- */
    /** 支付宝商户私钥 */
    public static PrivateKey PRIVATE_KEY = getPrivateKey();
    /** 需要抛出的异常 */
    private static Map<String, String> ERR_THROW = new HashMap<>();

    static {
        ERR_THROW.put("EXCEED_LIMIT_SM_AMOUNT", "请确认付款金额是否超限,付款金额必须大于支付宝的最低限额,必须小于支付宝的的最高限额");
        ERR_THROW.put("PRODUCT_NOT_SIGN", "支付宝该接口未签约,请签约产品之后再使用该接口");
    }

    /** ------------------------------R:必填----------------------------- */
    /** ------------------------------C:一定条件下可选--------------------- */
    /** ------------------------------O:可选----------------------------- */

    /**
     * 调起支付
     *
     * @param order
     * @param nofityUrl
     * @return
     * @throws Exception
     */
    public static AliResult payWeb(BaseOrder order, String nofityUrl) throws Exception {
        // 参数初始化
        init(order);

        // 获取支付宝接口参数
        AlipayTradePagePayModel param = new AlipayTradePagePayModel();
        param.setOutTradeNo(order.getBaseOrderNo());
        param.setProductCode(AliConstant.PRODUCT_CODE_VALUE_WEB_FAST_INSTANT_TRADE_PAY);
        param.setTotalAmount(order.getBaseOrderTotal().toString());
        param.setSubject(order.getBaseTitle());

        AlipayTradePagePayResponse res = AliPayApi.tradePage(param, nofityUrl, null);
        return pay(res);
    }

    /**
     * 调起支付
     *
     * @param order
     * @param nofityUrl
     * @return
     * @throws Exception
     */
    public static AliResult payApp(BaseOrder order, String nofityUrl) throws Exception {
        // 参数初始化
        init(order);

        // 获取支付宝接口参数
        AlipayTradeAppPayModel param = new AlipayTradeAppPayModel();
        param.setOutTradeNo(order.getBaseOrderNo());
        param.setTotalAmount(order.getBaseOrderTotal().toString());
        param.setSubject(order.getBaseTitle());

        AlipayTradeAppPayResponse res = AliPayApi.appPayToResponse(param, nofityUrl);
        return pay(res);
    }

    /**
     * 调起支付
     *
     * @param order
     * @param nofityUrl
     * @return
     * @throws Exception
     */
    public static AliResult payXcx(BaseOrder order, String nofityUrl) throws Exception {
        // 参数初始化
        init(order);

        // TODO
        return null;
    }

    /**
     * 调起支付
     *
     * @param order
     * @param nofityUrl
     * @return
     * @throws Exception
     */
    public static AliResult payWap(BaseOrder order, String nofityUrl) throws Exception {
        // 参数初始化
        init(order);

        // 获取支付宝接口参数
        AlipayTradeWapPayModel param = new AlipayTradeWapPayModel();
        param.setOutTradeNo(order.getBaseOrderNo());
        param.setProductCode(AliConstant.PRODUCT_CODE_VALUE_WAP_QUICK_WAP_WAY);
        param.setTotalAmount(order.getBaseOrderTotal().toString());
        param.setSubject(order.getBaseTitle());
        param.setQuitUrl(ServletUtil.getParamBody());

        AlipayTradeWapPayResponse res = AliPayApi.wapPay(param, nofityUrl, null);
        return pay(res);
    }

    /**
     * 参数初始化
     *
     * @param order
     * @return
     * @throws Exception
     */
    public static void init(BaseOrder order) throws Exception {
        if (StringUtil.isBlankObject(order.getBaseTitle())) {
            order.setBaseTitle(SystemParamBaseUtil.getStr(ConstantCache.WEB_NAME));
        }
    }

    /**
     * 调起支付
     *
     * @param res
     * @return
     */
    public static AliResult pay(AlipayResponse res) {
        Status status = getStatus(res);

        if (status.isFail()) {
            // 先查下是否已支付过
            // 调起支付宝支付
            // IJPayHttpResponse v3Get = WxPayApi.v3(RequestMethod.GET, WxDomain.CHINA.getType(),
            // String.format(WxApiType.ORDER_QUERY_BY_NO.getType(), findById.getOrderSn()) + "?mchid=" + mchid, mchid,
            // serialNo, null, WxUtil.PRIVATE_KEY, JSONObject.toJSONString(wxParam));
            // System.out.println(v3Get);
            ExpUtil.throwEx(status.getMsg());
        }

        // 返回结果
        AliResult result = new AliResult();
        result.setBody(res.getBody());
        return result;
    }

    /**
     * 退款
     *
     * @param order
     * @return
     * @throws Exception
     */
    public static Status refund(BaseOrder order) throws Exception {
        // 获取支付宝接口参数
        AlipayTradeRefundModel param = new AlipayTradeRefundModel();
        param.setOutTradeNo(order.getBaseOrderNo());
        param.setOutRequestNo(order.getBaseRefundNo());
        param.setRefundAmount(order.getBaseRefundTotal().toString());

        AlipayTradeRefundResponse res = AliPayApi.tradeRefundToResponse(param);
        return getStatus(res);
    }

    /**
     * 关闭订单
     *
     * @param orderNo   订单号
     * @return
     * @throws Exception
     */
    public static Status close(String orderNo) throws Exception {
        // 获取支付宝接口参数
        AlipayTradeCloseModel param = new AlipayTradeCloseModel();
        param.setOutTradeNo(orderNo);
        param.setOperatorId(TokenUtil.getUserId());

        AlipayTradeCloseResponse res = AliPayApi.tradeCloseToResponse(param);
        return getStatus(res);
    }

    /**
     * 查询支付订单
     *
     * @param orderNo   订单号
     * @return
     * @throws Exception
     */
    public static AlipayTradeQueryResponse queryByOrderNo(String orderNo) throws Exception {
        // 获取支付宝接口参数
        AlipayTradeQueryModel param = new AlipayTradeQueryModel();
        param.setOutTradeNo(orderNo);

        return AliPayApi.tradeQueryToResponse(param);
    }

    /**
     * 查询支付订单
     *
     * @param payNo 微信支付单号
     * @return
     * @throws Exception
     */
    public static AlipayTradeQueryResponse queryByPayNo(String payNo) throws Exception {
        // 获取支付宝接口参数
        AlipayTradeQueryModel param = new AlipayTradeQueryModel();
        param.setTradeNo(payNo);

        return AliPayApi.tradeQueryToResponse(param);
    }

    /**
     * 查询退款订单
     *
     * @param orderNo   商户订单号
     * @param refundNo  退款单号
     * @return
     * @throws Exception
     */
    public static AlipayTradeFastpayRefundQueryResponse queryRefund(String orderNo, String refundNo) throws Exception {
        // 获取支付宝接口参数
        AlipayTradeFastpayRefundQueryModel param = new AlipayTradeFastpayRefundQueryModel();
        param.setOutTradeNo(orderNo);
        param.setOutRequestNo(refundNo);

        return AliPayApi.tradeRefundQueryToResponse(param);
    }

    /**
     * 单笔转账
     *
     * @param orderNo   订单号
     * @param total     转账金额
     * @param openid    支付宝的唯一标识
     * @param name      用户姓名
     * @param title     转账原因
     * @return
     * @throws Exception
     */
    public static AlipayFundTransUniTransferResponse withdrawDeposit(String orderNo, BigDecimal total, String openid,
        String name, String title) throws Exception {

        // 获取支付宝接口参数
        Participant participant = new Participant();
        AlipayFundTransUniTransferModel param = new AlipayFundTransUniTransferModel();
        param.setOutBizNo(orderNo);
        param.setTransAmount(total.toString());
        param.setProductCode(AliConstant.PRODUCT_CODE_VALUE_ALL_TRANS_ACCOUNT_NO_PWD);
        param.setBizScene(AliConstant.BIZ_SCENE_VALUE_ALL_DIRECT_TRANSFER);
        param.setPayeeInfo(participant);
        participant.setIdentity(openid);
        participant.setIdentityType(AliConstant.IDENTITY_TYPE_VALUE_ALIPAY_USER_ID);
        participant.setName(name);

        return AliPayApi.uniTransferToResponse(param);
    }

    /**
     * 查询转账订单
     *
     * @param orderNo 商户订单号
     * @return
     * @throws Exception
     */
    public static AlipayFundTransOrderQueryResponse query(String orderNo) throws Exception {
        // 获取支付宝接口参数
        AlipayFundTransOrderQueryModel param = new AlipayFundTransOrderQueryModel();
        param.setOutBizNo(orderNo);

        return AliPayApi.transferQueryToResponse(param);
    }

    /**
     * 获取回调类型
     *
     * @return
     */
    public static int getCallBack(AliResponse res) throws Exception {
        int code = EAliCallBackType.UNKNOWN.getCode();
        // 基础数据
        String outTradeNo = res.getOutTradeNo();
        if (StringUtil.isBlank(outTradeNo)) {
            return code;
        }

        // 退款回调
        String outBizNo = res.getOutBizNo();
        BigDecimal refundFee = res.getRefundFee();
        if (StringUtil.isNotBlankListAll(outBizNo, refundFee)) {
            return EAliCallBackType.RETURN.getCode();
        }

        // 支付回调
        String tradeNo = res.getTradeNo();
        BigDecimal totalAmount = res.getTotalAmount();
        if (StringUtil.isNotBlankListAll(tradeNo, totalAmount)) {
            return EAliCallBackType.PAY.getCode();
        }

        return code;
    }

    /**
     * 验证签名
     *
     * @return
     * @throws Exception
     */
    public static boolean rsaCertCheckV1() throws Exception {
        Map<String, String> hashMap = null;
        String body = ServletUtil.getParamBody();
        if (StringUtil.isBlankString(body)) {
            hashMap = ServletUtil.getParameterMapString();
            if (StringUtil.isBlankObject(hashMap)) {
                return false;
            }
        } else {
            hashMap = new HashMap<>();
            body = StringUtil.decode(body);
            String[] split = body.split("&");
            for (String string : split) {
                int indexOf = string.indexOf("=");
                hashMap.put(string.substring(0, indexOf), string.substring(indexOf + 1));
            }
        }

        return rsaCertCheckV1(hashMap);
    }

    /**
     * 验证签名
     *
     * @param body
     * @return
     * @throws Exception
     */
    public static boolean rsaCertCheckV1Str(String body) throws Exception {
        Map<String, String> hashMap = null;
        hashMap = new HashMap<>();
        body = StringUtil.decode(body);
        String[] split = body.split("&");
        for (String string : split) {
            int indexOf = string.indexOf("=");
            hashMap.put(string.substring(0, indexOf), string.substring(indexOf + 1));
        }

        return rsaCertCheckV1(hashMap);
    }

    /**
     * 验证签名
     *
     * @param hashMap
     * @return
     * @throws Exception
     */
    public static boolean rsaCertCheckV1(Map<String, String> hashMap) throws Exception {
        return AlipaySignature.rsaCheckV1(hashMap,
            SystemParamBaseUtil.getStr(AliConstant.CA_CERT_CACHE_KEY_ALIPAY_CERT_PUBLIC),
            hashMap.get(AliConstant.FIELD_CHARSET), hashMap.get(AliConstant.FIELD_NAME_SIGN_TYPE));
    }

    /**
     * 验证签名并返回内容对象
     *
     * @return
     * @throws Exception
     */
    public static AliResponse rsaCertCheckV1GetRes() throws Exception {
        Map<String, String> hashMap = null;
        String body = ServletUtil.getParamBody();
        if (StringUtil.isBlankString(body)) {
            hashMap = ServletUtil.getParameterMapString();
            if (StringUtil.isBlankObject(hashMap)) {
                return null;
            }
        } else {
            hashMap = new HashMap<>();
            body = StringUtil.decode(body);
            String[] split = body.split("&");
            for (String string : split) {
                int indexOf = string.indexOf("=");
                hashMap.put(string.substring(0, indexOf), string.substring(indexOf + 1));
            }
        }

        return rsaCertCheckV1GetRes(hashMap);
    }

    /**
     * 验证签名并返回内容对象
     *
     * @param body
     * @return
     * @throws Exception
     */
    public static AliResponse rsaCertCheckV1StrGetRes(String body) throws Exception {
        Map<String, String> hashMap = null;
        hashMap = new HashMap<>();
        body = StringUtil.decode(body);
        String[] split = body.split("&");
        for (String string : split) {
            int indexOf = string.indexOf("=");
            hashMap.put(string.substring(0, indexOf), string.substring(indexOf + 1));
        }

        return rsaCertCheckV1GetRes(hashMap);
    }

    /**
     * 验证签名并返回内容对象
     *
     * @param hashMap
     * @return
     * @throws Exception
     */
    public static AliResponse rsaCertCheckV1GetRes(Map<String, String> hashMap) throws Exception {
        AliResponse jsonToObject = JsonUtil.jsonToObject(JsonUtil.objectToJson(hashMap), AliResponse.class);

        boolean rsaCertCheckV1 = AlipaySignature.rsaCheckV1(hashMap,
            SystemParamBaseUtil.getStr(AliConstant.CA_CERT_CACHE_KEY_ALIPAY_CERT_PUBLIC), jsonToObject.getCharset(),
            jsonToObject.getSignType());
        if (!rsaCertCheckV1) {
            return null;
        }

        return jsonToObject;
    }

    /**
     * 认证授权
     *
     * @return
     * @throws Exception
     */
    public static AlipayUserInfoAuthResponse login() throws Exception {
        String userId = TokenUtil.getUserId();
        CacheUtil.setex(AliConstant.STATE_CACHE_NAME + userId, userId,
            (int)(Constant.MILLIS_HOURS / Constant.MILLIS_SECOND));

        // 获取支付宝接口参数
        AlipayUserInfoAuthModel param = new AlipayUserInfoAuthModel();
        List<String> arrayList = new ArrayList<>();
        param.setScopes(arrayList);
        arrayList.add(AliConstant.SCOPES_VALUE_ALL_AUTH_BASE);
        param.setState(Base64Util.encrypt(userId));

        AlipayUserInfoAuthResponse res = AliPayApi.getAuthCode(param, IpConfig.getUrl() + "/payAli/updateInfo");
        return res;
    }

    /**
     * 获取用户信息
     *
     * @return
     * @throws Exception
     */
    public static AlipaySystemOauthTokenResponse getInfo(String state) throws Exception {
        String stateCache = CacheUtil.get(AliConstant.STATE_CACHE_NAME + state);
        if (!state.equals(stateCache)) {
            ExpUtil.throwEx("参数不匹配");
        }

        // 获取支付宝接口参数
        AlipaySystemOauthTokenRequest param = new AlipaySystemOauthTokenRequest();
        param.setGrantType(AliConstant.GRANT_TYPE_VALUE_ALL_AUTHORIZATION_CODE);
        param.setCode(ServletUtil.getParameter(AliConstant.FIELD_AUTH_CODE));

        AlipaySystemOauthTokenResponse res = AliPayApi.getInfo(param);
        return res;
    }

    /**
     * 判断接口是否调用成功
     *
     * @param res
     * @return
     */
    public static boolean isSuccess(AlipayResponse res) {
        String code = res.getCode();
        if ((isBlank(code) || AliConstant.SUCCESS_CODE.equals(code)) && res.isSuccess()) {
            return true;
        }

        return false;
    }

    /**
     * 获取接口调用结果
     *
     * @param res
     * @return
     */
    public static Status getStatus(AlipayResponse res) {
        int resCode = -1;
        String resMsg = null;
        String resSubCode = null;
        String resSubMsg = null;
        boolean isDispose = false;

        String code = res.getCode();
        String subCode = res.getSubCode();
        if (isNotBlank(subCode)) {
            resSubCode = subCode;
            resSubMsg = res.getSubMsg();
            resCode = TransErrorCode.FAIL.getCode();
            resMsg = subCode + "-" + resSubMsg;
            isDispose = true;
        } else if (isNotBlank(code)) {
            if (AliConstant.SUCCESS_CODE.equals(code)) {
                resCode = TransErrorCode.SUCCESS.getCode();
            } else {
                resCode = TransErrorCode.FAIL.getCode();
                resMsg = code + "-" + res.getMsg();
                isDispose = false;
            }
        } else {
            resCode = TransErrorCode.SUCCESS.getCode();
        }

        return new Status(resCode, resMsg, resSubCode, resSubMsg, isDispose);
    }

    /**
     * 获取接口调用结果
     *
     * @param res
     * @return
     */
    public static Status getStatusFundTrans(AlipayFundTransUniTransferResponse res) {
        Status status = getStatus(res);
        String errCode = res.getStatus();
        // 接口调用失败
        if (StringUtil.isNotBlankString(errCode)) {
            String msg = ERR_THROW.get(errCode);
            if (StringUtil.isNotBlankObject(msg)) {
                ExpUtil.throwEx(msg);
            }
        }
        // 接口调用成功但是支付状态不是成功
        if (status.isSuccess()) {
            if (AliConstant.WITHDRAW_DEPOSIT_STATUS_VALUE_DEALING.equals(errCode)) {
                status.setCode(TransErrorCode.ING.getCode());
            } else if (!AliConstant.WITHDRAW_DEPOSIT_STATUS_VALUE_SUCCESS.equals(errCode)) {
                status.setCode(TransErrorCode.FAIL.getCode());
                status.setMsg(res.getSubCode() + "-" + res.getSubMsg());
                status.setIsDispose(true);
            }
        }

        return status;
    }

    /**
     * 返回支付宝支付成功后的异步通知结果
     *
     * @param resMap
     * @return
     */
    public static String getRequestXml(TreeMap<String, String> resMap) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");

        Set<Entry<String, String>> entrySet = resMap.entrySet();
        Iterator<Entry<String, String>> iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Entry<String, String> next = iterator.next();
            String k = next.getKey();
            sb.append("<" + k + ">" + next.getValue() + "</" + k + ">");
        }

        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 设置支付宝证书私钥
     *
     * @return
     */
    public static void setPrivateKey() {
        // try {
        // PRIVATE_KEY = PayKit.getPrivateKey(AliConstant.CA_CERT_FILE_PATH_PEM_KEY);
        // } catch (Exception e) {
        // log.error(e.getMessage(), e);
        // }
    }

    /**
     * 获取支付宝证书私钥
     *
     * @return
     */
    private static PrivateKey getPrivateKey() {
        // try {
        // return PayKit.getPrivateKey(AliConstant.CA_CERT_FILE_PATH_PEM_KEY);
        // } catch (Exception e) {
        // log.error(e.getMessage(), e);
        // }
        return null;
    }

    /**
     * 获取默认Appid
     *
     * @return
     * @throws IOException
     */
    public static String getDefaultAppId() throws IOException {
        return getByMapStr(AliConstant.DEFAULT_APPID_CACHE_NAME);
    }

    /**
     * 获取网站应用Appid
     *
     * @return
     * @throws IOException
     */
    public static String getWebAppId() throws IOException {
        return getByMapStr(AliConstant.WEB_APPID_CACHE_NAME);
    }

    /**
     * 获取App应用Appid
     *
     * @return
     * @throws IOException
     */
    public static String getAppAppId() throws IOException {
        return getByMapStr(AliConstant.APP_APPID_CACHE_NAME);
    }

    /**
     * 获取小程序应用Appid
     *
     * @return
     * @throws IOException
     */
    public static String getXcxAppId() throws IOException {
        return getByMapStr(AliConstant.XCX_APPID_CACHE_NAME);
    }

    /**
     * 判断一个字符串是否是空(空对象和空字符串)
     *
     * @param str
     * @return
     */
    private static boolean isBlank(Object str) {
        return StringUtil.isBlank(str);
    }

    /**
     * 判断一个字符串是否不是空(空对象和空字符串)
     *
     * @param str
     * @return
     */
    private static boolean isNotBlank(Object str) {
        return StringUtil.isNotBlank(str);
    }

    /**
     * 从缓存中获取值
     *
     * @param key
     * @return
     * @throws IOException
     */
    private static String getByMapStr(String key) throws IOException {
        return SystemParamBaseUtil.getStr(key);
    }

}